import { IResLoader } from "../res/IResLoader";
import { UIManager } from "../ui/UIManager";
import { UIParam, IValid } from "../interfaces";
import { IEngine } from "../engine/IEngine";
import { ResType, IResItem } from "../res/ResDefine";
import { IUIView } from "../ui/IUIView";


export class ViewManager implements IValid {

    protected loader: IResLoader;
    protected ui: UIManager;
    protected engine: IEngine;
    constructor(ui: UIManager, loader: IResLoader, engine: IEngine) {
        this.ui = ui;
        this.engine = engine;
        this.loader = loader;
    }

    isValid() {
        return true;
    }

    //正在加载的ui，加载完后移除，提供判断是否有某个功能时使用。
    private loadingView: { [id: string]: UIParam } = {}
    private wailtMap: { [key: string]: boolean } = {}

    /**
     * 将一个预制体实例化后添加到UI管理器中。
     * @param prefab 
     * @param layerIndex 
     * @param className 
     */
    getView(prefab: IResItem, className?: any): IUIView {
        if (prefab) {
            let node = this.engine.instantiate(prefab.getRes())
            if (!node) {
                return null;
            }
            if (!className) {
                className = node.name;
            }
            let component: IUIView = this.engine.getComponent(node, className)
            if (component) {
                return component
            } else {
                console.warn(" comp is null ", className);
                return null;
            }
        } else {
            return null;
        }

    }

    /**
     * 将给定路径的预制体实例化后添加到UI管理器中。
     * 与pushView 的区别是 不受时间间隔影响。
     * @param path 
     * @param loader 
     * @param layerIndex 
     * @param func 
     * @param className 
     */
    loadView(path: string, func: (err: any, view: IUIView) => void, bundle: string = "", className?: any) {
        if (!className) {
            className = this.getClassName(path)
        }
        this.loadPrefab(path, bundle, (err, prefab: IResItem) => {
            if (err) {
                console.warn(" pushView err " + err + "  name " + prefab, path);
                func('error', null);
                return;
            }

            let component = this.getView(prefab, className)
            if (component) {
                // component.setPath(path)
                func(null, component);
            } else {
                console.warn(" comp is null ", className);
                func('error', null);
            }

        })
    }

    /**
    * 
    * @param param 
    */
    private pushUIView(param: UIParam, callback: (err: any, view: IUIView) => void) {
        let path = param.path;
        let canMulty = param.canMulty;
        if (this.wailtMap[path] && !canMulty) {
            console.warn(' pushVIew has path  ', path)
            callback(' wait please ', null)
            return;
        }
        let delayTime = param.delayTime | 500;
        this.wailtMap[path] = false;
        setTimeout(() => {
            this.wailtMap[path] = false;
        }, delayTime);
        this.loadView(path, (err: any, comp) => {

            if (err) {
                callback(err, null)
            } else {
                callback(null, comp)
            }
        }, param.bundle, param.className)
    }

    private pushUIToast(param: UIParam, callback: (err: any, view: IUIView) => void) {
        this.loadView(param.path, (err: any, view: IUIView) => {
            if (!err) {
                callback(null, view)
            } else {
                callback(" error " + param.path, null)
            }
        }, param.bundle, param.className)
    }
    /**
     * 
     * @param prefabName 
     * @param loader 
     * @param callback 
     */
    loadPrefab(prefabName: string, bundle: string, callback: (err: string, node?: any) => void) {
        let resName = prefabName;
        // console.log(' prefabName ', prefabName)
        if (prefabName.indexOf("/") >= 0) {
            let list = prefabName.split("/");
            prefabName = list[list.length - 1];
        }
        this.loader.loadRes({
            url: resName, type: ResType.Prefab, bundle: bundle, target: this, callback: (err: any, item: IResItem) => {
                if (err) {
                    callback(err, null)
                } else {
                    callback(null, item)
                }
            }
        })
        // .then((res: ResItem) => {
        //     callback(null, res)
        // }, (err) => {
        //     callback(err, null)
        // })
    }

    /**
     * 获得预制体上view组件的名称
     * 默认使用预制体文件名称
     * @param prefabName 
     */
    getClassName(prefabName: string) {
        let className = "";
        if (prefabName.indexOf("/") >= 0) {
            let list = prefabName.split("/");
            className = list[list.length - 1];
        } else {
            className = prefabName;
        }
        return className;
    }


    /**
    * 由controller创建的ui是否已经存在
    * @returns 
    */
    hasLayer(id: any): boolean {
        let param = this.loadingView[id]
        return param != undefined && param != null;
    }

    removeLayer(id: number): void {
        let param: UIParam = this.loadingView[id]
        if (param) {
            this.ui.removeByPath(param.path)
            this.removeLoadingView(id)
        }
    }

    closeLayer(layer: IUIView): void {
        let id = layer.getID();
        this.ui.removeView(layer)
        this.removeLoadingView(id)
    }

    clear() {
        this.loadingView = {}
        this.ui.clear()
    }
    /**
     * 关闭此控制器创建的ui。
     */
    private removeLoadingView(id: string | number) {
        if (this.loadingView[id]) {
            delete this.loadingView[id]
        }
    }



    popView() {
        let view: IUIView = this.ui.popView() as IUIView;
        if (view) {
            this.removeLoadingView(view.getID())
        }
    }

    pushToast(param: UIParam) {
        let callback = (view: IUIView) => {
            if (view) {
                view.setUIParam(param)
                view.setController(param.controller)
                view.setModel(param.model)
                this.ui.pushView(view)
            }
        }
        this.pushUIToast(param, callback)
    }

    pushView(param: UIParam, func?: (view: IUIView) => void) {
        let id = param.id;
        if (!id) {
            id = param.path;
        }
        param.id = id;
        if (this.hasLayer(id)) {
            return;
        }
        this.loadingView[id] = param;
        let callback = (err: any, view: IUIView) => {
            let id = param.id
            if (id) {
                if (!this.hasLayer(id)) {
                    return;
                }
            }
            if (!err) {
                view.setUIParam(param)
                if (param.controller) {
                    view.setController(param.controller)
                }
                if (param.model) {
                    view.setModel(param.model)
                }
                if (param.parent) {
                    this.removeLoadingView(id)
                    view.setParent(param.parent)
                } else {
                    this.ui.pushView(view)
                }
                if (func) {
                    func(view)
                }

            } else {
                this.removeLoadingView(id)
            }
        }
        this.pushUIView(param, callback)
    }
}